<template>
  <div>
    <el-card class="box-card">
      <!--el-card的默认插槽-->
      <!-- 卡片顶部添加品牌按钮 v-has是全局自定义指令:实现按钮的权限管理 -->
      <el-button type="primary" size="default" icon="Plus" @click="addTrademark" v-has="`btn.Trademark.add`">添加品牌</el-button>
      <!-- 表格组件：用于展示已有的品牌数据 -->
      <!-- table -> border:可以设置表格纵向是否有边框。 table-column -> label:设置这列标题 ---width:设置这列宽度 ---align:设置这列对齐方式（默认left） -->
      <!-- data:需要展示的数据(数组) -->
      <el-table style="margin: 10px 0" border :data="trademarkArr">
        <!--type:如果设置为index, 则显示该行的索引（从 1 开始计算）-->
        <el-table-column label="序号" width="80px" align="center" type="index"></el-table-column>
        <!-- table-column:默认展示数据是用div,也可以使用作用域插槽自定义列的内容。 prop:每个对象对应列内容的字段名 -->
        <el-table-column label="品牌名称" prop="tmName"></el-table-column>
        <el-table-column label="品牌LOGO">
          <!--row:el-table-column子组件回传的每一个品牌对象(使用对象解构), 这里只能使用作用域插槽, 不能使用prop, 否则只能显示图片的字符串地址, 而不是真正显示图片-->
          <template v-slot:default="{ row }">
            <img :src="row.logoUrl" style="width: 100px; height: 100px" alt="" />
          </template>
        </el-table-column>
        <el-table-column label="品牌操作">
          <template v-slot:default="{ row }">
            <el-button type="primary" size="small" icon="Edit" @click="updateTrademark(row)"></el-button>
            <!--气泡确认框-->
            <el-popconfirm :title="`您确定要删除${row.tmName}?`" width="250px" icon="Delete" @confirm="removeTradeMark(row.id)">
              <!--具名插槽-->
              <template #reference>
                <el-button type="primary" size="small" icon="Delete"></el-button>
              </template>
            </el-popconfirm>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页器组件 pagination
               v-model:current-page->设置分页器当前页码(v-model主要实现父子组件数据的双向绑定(同步))--特别注意:子组件会给父组件的自定义事件函数注入当前页(同时自定义事件函数内部也使用了)
               v-model:page-size->设置每页展示数据条数--特别注意:子组件会给父组件的自定义事件函数注入当前每页条数(但没使用)
               page-sizes->用于设置每页展示数据条数的下拉菜单的数据
               background->设置分页器按钮是否有背景颜色(蓝色)
               layout->可以设置分页器六个子组件布局, 另外'->'代表:把后面的子组件顶到最右侧
               total->总条目数， total 和 page-count 设置任意一个就可以达到显示页码的功能；如果要支持 page-sizes 的更改，则需要使用 total 属性
               pager-count->设置最大页码按钮数。 页码按钮的数量，当总页数超过该值时会折叠
               @size-change->page-size 改变时触发
               @current-change->current-page 改变时触发
      -->
      <el-pagination
        @size-change="sizeChange"
        @current-change="getHasTrademark"
        :pager-count="9"
        v-model:current-page="pageNo"
        v-model:page-size="limit"
        :page-sizes="[3, 5, 7, 9]"
        :background="true"
        layout="prev, pager, next, jumper,->,sizes,total"
        :total="total"
      />
    </el-card>
    <!-- 对话框组件:在添加品牌与修改已有品牌业务的时候使用 -->
    <!-- v-model:控制对话框的显示与隐藏： true显示 false隐藏。 title:设置对话框左上角标题 -->
    <el-dialog v-model="dialogFormVisible" :title="trademarkParams.id ? '修改品牌' : '添加品牌'">
      <!--表单校验: ①model: 表单数据对象 ②rules: 表单验证规则对象 ③prop: model的校验项键名 ④ref：获取表单实例，目的是做整个表单的校验-->
      <el-form style="width: 80%" :model="trademarkParams" :rules="rules" ref="formRef">
        <!--label-width：表单项标签的宽度，多个表单项设置相同的值，则标签会右对齐。如果不设置，则标签会左对齐，这样表单项内容就不会对齐。-->
        <el-form-item label="品牌名称" label-width="100px" prop="tmName">
          <el-input placeholder="请您输入品牌名称" v-model="trademarkParams.tmName"></el-input>
        </el-form-item>
        <el-form-item label="品牌LOGO" label-width="100px" prop="logoUrl">
          <!-- upload组件的action属性:图片上传路径带上/api,否则代理服务器不发送这次post请求 -->
          <el-upload class="avatar-uploader" action="/api/admin/product/fileUpload" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload">
            <img v-if="trademarkParams.logoUrl" :src="trademarkParams.logoUrl" class="avatar" alt="" />
            <el-icon v-else class="avatar-uploader-icon">
              <Plus />
            </el-icon>
          </el-upload>
        </el-form-item>
      </el-form>
      <!-- 底部的具名插槽:footer -->
      <template #footer>
        <el-button type="primary" size="default" @click="cancel">取消</el-button>
        <el-button type="primary" size="default" @click="confirm">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ElMessage, UploadProps } from 'element-plus'
import { ref, onMounted, reactive, nextTick } from 'vue'
import { reqHasTrademark, reqAddOrUpdateTrademark, reqDeleteTrademark } from '@/api/product/trademark'
import type { Records, TradeMarkResponseData, TradeMark } from '@/api/product/trademark/type'

//当前页码
let pageNo = ref<number>(1)
//每页展示数据条数
let limit = ref<number>(7)
//存储已有品牌数据的总数
let total = ref<number>(0)
//存储已有品牌的数据(数组)
let trademarkArr = ref<Records>([])
//控制对话框显示与隐藏
let dialogFormVisible = ref<boolean>(false)
//定义收集新增品牌数据
let trademarkParams = reactive<TradeMark>({
  tmName: '',
  logoUrl: '',
})
//获取el-form组件实例
let formRef = ref()

//把获取已有品牌的接口封装为一个函数, 这样在任何情况下想获取数据,调用此函数即可
const getHasTrademark = async (pager = 1) => {
  //当前页码
  pageNo.value = pager
  let result: TradeMarkResponseData = await reqHasTrademark(pageNo.value, limit.value)
  if (result.code == 200) {
    // 存储已有品牌总个数
    total.value = result.data.total
    // 存储查到的数据
    trademarkArr.value = result.data.records
  }
}

//组件挂载完毕的钩子---发一次请求,获取第一页、一页七个已有品牌数据
onMounted(() => {
  getHasTrademark()
})

//分页器当前页码发生变化的时候会触发
//对于当前页码发生变化这个自定义事件,子组件pagination会给父组件回传数据(当前访问的页码)
/*
const changePageNo = (pageNo:number)=>{
    // 当前访问的页码
    console.log(pageNo)
    // 当前页码发生变化的时候,再次发请求获取对应页面已有品牌数据, 并展示
    getHasTrademark();
}
 */

//当下拉菜单发生变化的时候会触发
//对于下拉菜单发生变化这个自定义事件,子组件pagination会给父组件回传数据(当前访问的每页条数)
const sizeChange = () => {
  //当前每一页的数据量发生变化的时候，当前页码归1
  // pageNo.value = 1
  getHasTrademark()
}

//添加品牌按钮的回调
const addTrademark = () => {
  //对话框显示
  dialogFormVisible.value = true
  //清空收集数据 -- 这里id也需要清空(避免先点修改,再点添加,id仍然保存的情况)
  trademarkParams.id = 0
  trademarkParams.tmName = ''
  trademarkParams.logoUrl = ''
  // 清空校验规则错误提示信息
  // 第一种写法:ts的问号语法 -- ？表示：如果formRef存在（对话框已经打开过，有可能需要清理校验信息），则执行clearValidate；如果formRef不存在（对话框从来没打开过， 根本就不会出现校验信息，所以也不需要清理），则不执行clearValidate(强制执行会报错)
  /*
  formRef.value?.clearValidate('tmName')
  formRef.value?.clearValidate('logoUrl')
   */
  // 第二种写法:nextTick语法 -- 当响应式数据发生变化时,获取到更新后的DOM,即确保在下一个DOM更新循环中执行代码
  nextTick(() => {
    formRef.value.clearValidate('tmName')
    formRef.value.clearValidate('logoUrl')
  })
}
//修改已有品牌按钮的回调 row:当前已有的品牌对象
const updateTrademark = (row: TradeMark) => {
  //对话框显示
  dialogFormVisible.value = true
  //回显已有品牌数据 复制对象(ES6语法)
  Object.assign(trademarkParams, row)
  // 清空校验规则错误提示信息
  nextTick(() => {
    formRef.value.clearValidate('tmName')
    formRef.value.clearValidate('logoUrl')
  })
}

//对话框底部取消按钮的回调
const cancel = () => {
  //对话框隐藏
  dialogFormVisible.value = false
}
//对话框底部确定按钮的回调
const confirm = async () => {
  // validate返回promise对象: 表单项全部校验成功,返回成功的promise。任意一个表单项校验不成功,返回失败的promise。
  // 在发请求之前,要对于整个表单进行校验（包括图片的自定义校验）。调用validate这个方法进行全部表单相校验,如果校验全部通过，再执行后面的语法。如果任意一个表单项校验不成功，啥也不做（await等待的是成功的promise，相当于确定按钮空点了一次）。
  // 这里也不需要nextTick包裹, 因为这时可以点到确定按钮, 肯定已经有了formRef
  await formRef.value.validate()

  let result: any = await reqAddOrUpdateTrademark(trademarkParams)
  //添加|修改已有品牌
  if (result.code == 200) {
    //对话框隐藏
    dialogFormVisible.value = false
    //弹出成功提示信息
    ElMessage({
      type: 'success',
      message: trademarkParams.id ? '修改品牌成功' : '添加品牌成功',
    })
    //再次发请求获取已有全部的品牌数据 -- 修改后留在当前页 添加后留在第一页
    await getHasTrademark(trademarkParams.id ? pageNo.value : 1)
  } else {
    //对话框隐藏
    dialogFormVisible.value = false
    //弹出失败提示信息
    ElMessage({
      type: 'error',
      message: trademarkParams.id ? '修改品牌失败' : '添加品牌失败',
    })
  }
}

//上传图片组件->上传图片之前触发的钩子函数
const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
  //钩子是在图片上传文件之前触发 -> 可以约束上传文件的类型与大小, rawFile是上传图片组件注入的文件对象
  //要求:上传文件格式png|jpg|gif <4M  返回true->允许上传 返回false->阻止上传
  if (rawFile.type == 'image/png' || rawFile.type == 'image/jpeg' || rawFile.type == 'image/gif') {
    // size的单位是byte(字节)
    if (rawFile.size / 1024 / 1024 < 4) {
      return true
    } else {
      ElMessage({
        type: 'error',
        message: '上传文件大小必须小于4M',
      })
      return false
    }
  } else {
    ElMessage({
      type: 'error',
      message: '上传文件格式必须是PNG|JPG|GIF',
    })
    return false
  }
}
//上传图片组件->图片上传成功的钩子函数
const handleAvatarSuccess: UploadProps['onSuccess'] = (response) => {
  //response:即为当前这次上传图片post请求服务器返回的数据
  trademarkParams.logoUrl = response.data //收集上传图片的地址,添加一个新的品牌的时候带给服务器
  //图片上传成功,清除掉以前的图片校验信息, 这里不需要nextTick包裹, 因为刚刚图片上传完成, 肯定已经有了formRef
  formRef.value.clearValidate('logoUrl')
}

//品牌名称自定义校验规则的方法 -- rule：规则对象，value：表单收集的内容， callBack：放行函数
const validatorTmName = (rule: any, value: any, callBack: any) => {
  //当前表单元素失去焦点的时候,会触发此方法。自定义校验规则
  if (value.trim().length >= 2) {
    callBack()
  } else {
    //校验未通过返回的错误提示信息
    callBack(new Error('品牌名称位数大于等于两位'))
  }
}
//品牌LOGO图片自定义校验规则的方法 -- value：上传成功图片的地址字符串
const validatorLogoUrl = (rule: any, value: any, callBack: any) => {
  if (value) {
    // 说明图片已经上传成功
    callBack()
  } else {
    callBack(new Error('LOGO图片必须上传'))
  }
}
//表单校验规则对象
const rules = {
  //required:这个字段必须校验(true),表单项前面出现五角星, trigger:代表触发校验规则的时机[blur、change]（对图片上传校验无效--只能依赖validate触发校验）, validator:自定义校验规则的方法
  tmName: [{ required: true, trigger: 'blur', validator: validatorTmName }],
  logoUrl: [{ required: true, validator: validatorLogoUrl }],
}

//气泡确认框确定按钮的回调
const removeTradeMark = async (id: number) => {
  //发出删除已有品牌的请求
  let result = await reqDeleteTrademark(id)
  if (result.code == 200) {
    //删除成功提示信息
    ElMessage({
      type: 'success',
      message: '删除品牌成功',
    })
    //再次获取已有的品牌数据
    await getHasTrademark(trademarkArr.value.length > 1 ? pageNo.value : pageNo.value - 1)
  } else {
    ElMessage({
      type: 'error',
      message: '删除品牌失败',
    })
  }
}
</script>
<style scoped>
.avatar-uploader .avatar {
  width: 178px;
  height: 178px;
  display: block;
}
</style>

<style>
/*以下都是上传图片框的样式*/
.avatar-uploader .el-upload {
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: var(--el-transition-duration-fast);
}
.avatar-uploader .el-upload:hover {
  border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  text-align: center;
}
</style>
